package tools

import (
	"fmt"
	"gozeroStandalone/internal/config"
	"sync"

	configurator "github.com/zeromicro/go-zero/core/configcenter"
	"github.com/zeromicro/go-zero/core/configcenter/subscriber"
)

/*
 +----------------------------------------------------------------------
 + Title        : configCenter
 + Author       : huwl
 + Version      : 1.0.0
 + Initial-Time : 2021-09-22
 + Last-time    : 这个文件最后修改的时间 + 修改人的名称
 + Desc         : 配置中心
 +----------------------------------------------------------------------
*/

var (
	configCenterInstance *ConfigCenterInstance
	configCenterOnce     sync.Once
	configCenterInitErr  error
)

type ConfigCenterInstance struct {
	etcdSubscriber subscriber.Subscriber
	conf           config.Config
}

type MysqlConfig struct {
	DataSource string `yaml:"data_source" json:"data_source"`
}

// 配置中心数据
type ConfigCenterData struct {
	MysqlConf MysqlConfig       `yaml:"mysql_conf" json:"mysql_conf"`
	MongoConf MongoConfig       `yaml:"mongo_conf" json:"mongo_conf"`
	RedisConf RedisConfig       `yaml:"redis_conf" json:"redis_conf"`
	EsConf    ElasticSearchConf `yaml:"es_conf" json:"es_conf"`
}

/**
 * 创建配置中心实例
 *
 * @param *svc.ServiceContext svcCtx ----------------必传
 * @return *ConfigCenterInstance, error
 * @author huwl
 */
func NewConfigCenterInstance(conf config.Config) (*ConfigCenterInstance, error) {
	// 组装ETCD配置
	ec := subscriber.EtcdConf{}
	switch conf.Env {
	case "dev":
		ec.Hosts = conf.EtcdConf.Dev.Hosts
		ec.User = conf.EtcdConf.Dev.Username
		ec.Pass = conf.EtcdConf.Dev.Password
		ec.Key = conf.EtcdConf.Dev.Prefix
	case "test":
		ec.Hosts = conf.EtcdConf.Test.Hosts
		ec.User = conf.EtcdConf.Test.Username
		ec.Pass = conf.EtcdConf.Test.Password
		ec.Key = conf.EtcdConf.Test.Prefix
	case "prod":
		ec.Hosts = conf.EtcdConf.Prod.Hosts
		ec.User = conf.EtcdConf.Prod.Username
		ec.Pass = conf.EtcdConf.Prod.Password
		ec.Key = conf.EtcdConf.Prod.Prefix
	}

	// 创建 etcd subscriber
	etcdSubscriber, err := subscriber.NewEtcdSubscriber(ec)
	if err != nil {
		return nil, err
	}

	return &ConfigCenterInstance{
		etcdSubscriber,
		conf,
	}, nil
}

/**
 * 获取单列模式：配置中心实例
 *
 * @param *svc.ServiceContext svcCtx ----------------必传
 * @return *ConfigCenterInstance, error
 * @author huwl
 */
func GetSingleConfigCenterInstance(c config.Config) (*ConfigCenterInstance, error) {
	configCenterOnce.Do(func() {
		configCenterInstance, configCenterInitErr = NewConfigCenterInstance(c)
	})
	return configCenterInstance, configCenterInitErr
}

/**
 * 获取配置中心数据
 *
 * @param any configData ----------------------------必传
 * @param string configName -------------------------必传
 * @return error
 * @author huwl
 */
func (cci *ConfigCenterInstance) GetConfigCenter(configName string, configData any) {
	// 创建 configurator
	cc := configurator.MustNewConfigCenter[ConfigCenterData](configurator.Config{
		Type: cci.conf.EtcdConf.Type, // 配置值类型：json,yaml,toml
	}, cci.etcdSubscriber)
	// 获取配置
	// 注意: 配置如果发生变更，调用的结果永远获取到最新的配置
	v, err := cc.GetConfig()
	if err != nil {
		panic(err)
	}

	switch configName {
	case cci.conf.ConfigCenterNameConf.MysqlConfigName:
		// 使用反射或类型断言来设置值
		if mysqlConf, ok := configData.(*MysqlConfig); ok {
			*mysqlConf = v.MysqlConf
		} else {
			panic("invalid config center data type for mysql...")
		}
	case cci.conf.ConfigCenterNameConf.MongoConfigName:
		// 使用反射或类型断言来设置值
		if mongoConf, ok := configData.(*MongoConfig); ok {
			*mongoConf = v.MongoConf
		} else {
			panic("invalid config center data type for mongo...")
		}
	case cci.conf.ConfigCenterNameConf.RedisConfigName:
		// 使用反射或类型断言来设置值
		if redisConfig, ok := configData.(*RedisConfig); ok {
			*redisConfig = v.RedisConf
		} else {
			panic("invalid config center data type for redis...")
		}
	case cci.conf.ConfigCenterNameConf.EsConfigName:
		// 使用反射或类型断言来设置值
		if esConfig, ok := configData.(*ElasticSearchConf); ok {
			*esConfig = v.EsConf
			ResetRedisInstance()
		} else {
			panic("invalid config center data type for elasticsearch...")
		}
	}

	// 如果想监听配置变化，可以添加 listener
	// 此处获取的配置数据有时候返回的是旧数据？？？？
	cc.AddListener(func() {
		v, err := cc.GetConfig()
		if err != nil {
			panic(err)
		}

		fmt.Println("etcd add listener value ========== ", v)

		switch configName {
		case cci.conf.ConfigCenterNameConf.MysqlConfigName:
			// 使用反射或类型断言来设置值
			if mysqlConf, ok := configData.(*MysqlConfig); ok {
				*mysqlConf = v.MysqlConf
			} else {
				panic("invalid config center data type for mysql...")
			}
		case cci.conf.ConfigCenterNameConf.MongoConfigName:
			// 使用反射或类型断言来设置值
			if mongoConf, ok := configData.(*MongoConfig); ok {
				*mongoConf = v.MongoConf
				CloseMongo()
			} else {
				panic("invalid config center data type for mongo...")
			}
		case cci.conf.ConfigCenterNameConf.RedisConfigName:
			// 使用反射或类型断言来设置值
			if redisConfig, ok := configData.(*RedisConfig); ok {
				*redisConfig = v.RedisConf
				ResetRedisInstance()
			} else {
				panic("invalid config center data type for redis...")
			}
		case cci.conf.ConfigCenterNameConf.EsConfigName:
			// 使用反射或类型断言来设置值
			if esConfig, ok := configData.(*ElasticSearchConf); ok {
				*esConfig = v.EsConf
				CloseElasticSearch()
			} else {
				panic("invalid config center data type for elasticsearch...")
			}
		}
	})

	// select {}
}
